package com.sk.seckilldemo.service;

import com.sk.seckilldemo.entity.SeckillActivity;
import com.sk.seckilldemo.entity.SeckillMessage;
import com.sk.seckilldemo.entity.request.SeckillRequest;
import com.sk.seckilldemo.mapper.SeckillActivityMapper;
import com.sk.seckilldemo.util.Response;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 秒杀预热活动实现类
 * */
@Service
@RequiredArgsConstructor
public class SeckillService {
    // 新建Redis数据模板
    private final RedisTemplate<String,Object> redisTemplate;
    // 新建RabbitMQ消息模板
    private final RabbitTemplate rabbitTemplate;
    // 引入秒杀预热活动，加载接口实现
    private final SeckillActivityMapper activityMapper;

    /**
     * 秒杀活动预热
     * */
    public void prepareSeckill(Integer activityId){
        //根据指定ID获取相关秒杀活动的信息
        SeckillActivity activity = activityMapper.selectById(activityId);

        // 将库存存入Redis
        String stockKey = "seckill:stock:" + activityId;
        redisTemplate.opsForValue().set(stockKey,activity.getStock());

        // 设置活动开始和结束时间
        String startKey = "seckill:start:" + activityId;
        String endKey = "seckill:end:" + activityId;
        redisTemplate.opsForValue().set(startKey,activity.getStartTime().toString());
        redisTemplate.opsForValue().set(endKey,activity.getEndTime().toString());
    }

    /**
     * 秒杀请求处理
     * */
    public Response handleSeckill(SeckillRequest seckillRequest){

        // 1. 校验活动时间
        if(!isActivityValid(seckillRequest.getActivityId())){
            return new Response(501,"活动未开始或已结束",null);
        }

        // 2. 验证用户是否已经参加过活动
        if(hasParticipated(seckillRequest)){
            return new Response(502,"您已经参与过本次活动",null);
        }

        // 3. Redis预减库存
        Long stock = redisTemplate.opsForValue().decrement("seckill:stock:"+seckillRequest.getActivityId());

        if(stock == null || stock <0){
            // 库存不足，恢复库存
            redisTemplate.opsForValue().increment("seckill:stock:" + seckillRequest.getActivityId());
            return new Response(503,"商品已售罄",null);
        }

        // 4.生成秒杀消息
        SeckillMessage message = new SeckillMessage(seckillRequest.getUser_id(),seckillRequest.getActivityId());

        // 5. 将秒杀消息发送到RabbitMQ
        rabbitTemplate.convertAndSend(
                "seckill.exchange",
                "seckill.routing.key",
                message,
                m->{
                    // 设置消息过期时间
                    m.getMessageProperties().setExpiration("10000");
                    return m;
                });

        return new Response(200,"秒杀请求已接收，正在处理...",null);
    }

    /**
     * 校验秒杀活动时间
     * */
    private boolean isActivityValid(Integer activityId){
        // 从Redis服务器中获取活动时间，通过key进行获取
        String startKey = "seckill:start:" + activityId;
        // 获取秒杀活动开始时间字符串
        String startTimeStr = (String) redisTemplate.opsForValue().get(startKey);

        // 获取秒杀活动开始时间字符串
        String endKey = "seckill:end:" + activityId;
        String endTimeStr = (String) redisTemplate.opsForValue().get(endKey);

        // 判断获取的开始和结束时间不能为空
        if(startTimeStr == null || endTimeStr == null){
            return false;
        }

        // 尝试获取当前时间，并将从Redis获取的开始和结束时间转换成时间类型进行比较
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
            LocalDateTime startTime = LocalDateTime.parse(startTimeStr,formatter);
            LocalDateTime endTime = LocalDateTime.parse(endTimeStr, formatter);
            LocalDateTime now = LocalDateTime.now();

            return now.isAfter(startTime) && now.isBefore(endTime);
        }catch (Exception e){
            System.out.println("解析活动时间异常：" + e);
            return false;
        }
    }

    /**
     * 检查当前用户是否已经参加过活动
     * */
    private boolean hasParticipated(SeckillRequest seckillRequest){
        String key = "seckill:user:" + seckillRequest.getActivityId()+":" + seckillRequest.getUser_id();
        return redisTemplate.hasKey(key);
    }

    /**
     * 标记用户已参加过活动
     * */
    private void markUserParticipated(SeckillRequest seckillRequest){
        String key = "seckill:user:" + seckillRequest.getActivityId()+":" + seckillRequest.getUser_id();
        // 设置2小时过期，防止长期占用内存
        redisTemplate.opsForValue().set(key,"1", Duration.ofHours(2));
    }
}
